/**
 * Copyright (c) 2016, 2024, All rights reserved.
 * Powered by [liangyafeng@duplicall.com] On 2024-03-21 15:31:31
 */
package com.yafengliang.controller;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;

import javax.sound.sampled.*;
import java.io.*;
import java.util.Base64;

/**
 * <p>Project: SpringBoot - McirophoneExample
 * <p>@author liangyafeng@duplicall.com On 2024-03-21 15:31:31
 *
 * @Description
 */
@Slf4j
public class MicrophoneExample {

    public static void main(String[] args) {
        // 定义音频格式
        AudioFormat format = new AudioFormat(
                AudioFormat.Encoding.PCM_SIGNED,
                44100, // 采样率
                16, // 单声道
                4, // 每个样本的字节数 = 2个字节/样本，1个字节/16位
                2, // 每个帧的字节数 = 每个样本的字节数 * 单声道数
                44100, // 通常与采样率相同
                false // 不使用定点
        );
        // 目标数据行，用于传输音频数据
        TargetDataLine line;
        // 缓冲区，用于存储音频数据
        byte[] buffer;

        try {
            // 获取线路
            DataLine.Info info = new DataLine.Info(TargetDataLine.class, format);
            line = (TargetDataLine) AudioSystem.getLine(info);
            line.open(format);

            // 为缓冲区分配大小
            buffer = new byte[line.getBufferSize() / 5]; // 只捕获10秒钟的数据

            // 打开麦克风进行录音
            line.start();

            // 读取数据
            int numBytesRead = 0;
            while (numBytesRead < buffer.length) {
                int bytesRead = line.read(buffer, numBytesRead, buffer.length - numBytesRead);
                if (bytesRead == -1) {
                    break;
                }
                numBytesRead += bytesRead;
            }

            // 停止录音
            line.stop();
            line.close();

            // 处理音频数据buffer...
            createWAVFile(buffer, String.valueOf(System.currentTimeMillis()));
        } catch (LineUnavailableException | IllegalArgumentException exception) {
            exception.printStackTrace();
        }
    }

    private static void createWAVFile(byte[] audioData, String fileName) {
        if (audioData != null){
            AudioInput audioInput = new AudioInput();
            audioInput.setAudioformat("PCM16");
            byte[] byteHeader = createWavHeader(audioData.length);
            byte[] totalBytes = ArrayUtils.addAll(byteHeader, audioData);
            audioInput.setAudioBase64(Base64.getEncoder().encodeToString(totalBytes));
            writeFile(totalBytes,fileName);
            audioInput.setSamplingRate(8000);
            audioInput.setSecondsThreshold(0);
        }
    }

    private static void writeFile(byte[] totalBytes, String fileName) {
        try {
            String filename = "c:\\" + fileName + ".wav";
            File target = new File(filename);
            if (target.exists()) {
                target.delete();
            }
            DataOutputStream out = new DataOutputStream(new FileOutputStream(filename, true));
            out.write(totalBytes);
            out.close();
        } catch (IOException e) {
            log.error(e.getMessage());
        }
    }

    private static byte[] createWavHeader(int length) {
        WaveHeader waveHeader = new WaveHeader();
        waveHeader.fileLength = length+(44-8);
        waveHeader.FmtHdrLeth = 16;
        waveHeader.BitsPerSample = 16;
        waveHeader.Channels = 1;
        waveHeader.FormatTag = 0x0001;
        waveHeader.SamplesPerSec = 8000;
        waveHeader.BlockAlign = (short) (waveHeader.Channels * waveHeader.BitsPerSample / 8);
        waveHeader.AvgBytesPerSec = waveHeader.BlockAlign * waveHeader.SamplesPerSec;
        waveHeader.DataHdrLeth = length;
        byte[] bytesHeader = new byte[0];
        try {
            bytesHeader = waveHeader.getHeader();
        }catch (Exception e){
            log.error("bytesHeader = waveHeader.getHeader() error:{}",e.toString());
        }
        return bytesHeader;
    }


    @Data
    public static class AudioInput {
        private int samplingRate;
        private String audioformat;
        private int secondsThreshold;
        private String audioBase64;
    }

    public static class WaveHeader {
        public final char fileID[] = {'R', 'I', 'F', 'F'};
        public int fileLength;
        public char wavTag[] = {'W', 'A', 'V', 'E'};
        public char FmtHdrID[] = {'f', 'm', 't', ' '};
        public int FmtHdrLeth;
        public short FormatTag;
        public short Channels;
        public int SamplesPerSec;
        public int AvgBytesPerSec;
        public short BlockAlign;
        public short BitsPerSample;
        public char DataHdrID[] = {'d', 'a', 't', 'a'};
        public int DataHdrLeth;

        public byte[] getHeader() throws IOException {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            writeChar(bos, fileID);
            writeInt(bos, fileLength);
            writeChar(bos, wavTag);
            writeChar(bos, FmtHdrID);
            writeInt(bos, FmtHdrLeth);
            writeShort(bos, FormatTag);
            writeShort(bos, Channels);
            writeInt(bos, SamplesPerSec);
            writeInt(bos, AvgBytesPerSec);
            writeShort(bos, BlockAlign);
            writeShort(bos, BitsPerSample);
            writeChar(bos, DataHdrID);
            writeInt(bos, DataHdrLeth);
            bos.flush();
            byte[] r = bos.toByteArray();
            bos.close();
            return r;
        }

        private void writeShort(ByteArrayOutputStream bos, int s) throws IOException {
            byte[] mybyte = new byte[2];
            mybyte[1] = (byte) ((s << 16) >> 24);
            mybyte[0] = (byte) ((s << 24) >> 24);
            bos.write(mybyte);
        }

        private void writeInt(ByteArrayOutputStream bos, int n) throws IOException {
            byte[] buf = new byte[4];
            buf[3] = (byte) (n >> 24);
            buf[2] = (byte) ((n << 8) >> 24);
            buf[1] = (byte) ((n << 16) >> 24);
            buf[0] = (byte) ((n << 24) >> 24);
            bos.write(buf);
        }

        private void writeChar(ByteArrayOutputStream bos, char[] id) {
            for (int i = 0; i < id.length; i++) {
                char c = id[i];
                bos.write(c);
            }
        }
    }


}
